//
//  YuanXinNetWorking.m
//  YuanXinNetWorking
//
//  Created by 晏德智 on 2016/12/7.
//  Copyright © 2016年 晏德智. All rights reserved.
//

#import "YuanXinNetworkingManager.h"

#if __has_include(<React/RCTBridge.h>)
#import <React/RCTConvert.h>
#import <React/RCTBridge.h>
#import <React/RCTUtils.h>
#import <React/RCTImageLoader.h>
#else
#import <React/RCTConvert.h>
#import <React/RCTBridge.h>
#import <React/RCTUtils.h>
#import <React/RCTImageLoader.h>
#endif

#import "NetworkingManager.h"
#import "RCTConvert+Networking.h"

#import "YuanXinUploadFiles.h"
#import "UploadFileLoader.h"
#import "YuanXinImageCache.h"

@implementation YuanXinNetworkingManager

RCT_EXPORT_MODULE();

@synthesize bridge = _bridge;

- (dispatch_queue_t)methodQueue
{
    return ossMethodQueue();
}

static dispatch_queue_t ossMethodQueue()
{
    // We want all instances to share the same queue since they will be reading/writing the same files.
    static dispatch_queue_t queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        queue = dispatch_queue_create("YuanXinNetworking.RequestQueue", DISPATCH_QUEUE_CONCURRENT);
    });
    return queue;
}

RCT_EXPORT_METHOD(httGet:(NSString *)url
                  parameters:(id)parameters
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    
    __block RCTPromiseResolveBlock successCallback;
    if(resolve){
        successCallback = [resolve copy];
    }
    
    __block RCTPromiseRejectBlock errorCallback;
    if(reject){
        errorCallback = [reject copy];
    }
    
    [[NetworkingManager shareInstance] GET:url
                                parameters:parameters
                                   success:^(NSURLSessionDataTask *task, id _Nullable responseObject){
                                       if(successCallback){
                                           successCallback(responseObject);
                                       }
                                   }
                                   failure:^(NSURLSessionDataTask * _Nullable task, NSError *error){
                                       if(errorCallback){
                                           errorCallback(@(error.code),error.localizedDescription,error);
                                       }
                                   }];
}


RCT_EXPORT_METHOD(httpPost:(NSString *)url
                  parameters:(id)parameters
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    
    __block RCTPromiseResolveBlock successCallback;
    if(resolve){
        successCallback = [resolve copy];
    }
    
    __block RCTPromiseRejectBlock errorCallback;
    if(reject){
        errorCallback = [reject copy];
    }
    
    [[NetworkingManager shareInstance] POST:(NSString *)url
                                 parameters:parameters
                                    success:^(NSURLSessionDataTask *task, id _Nullable responseObject){
                                        if(successCallback){
                                            successCallback(responseObject);
                                        }
                                    }
                                    failure:^(NSURLSessionDataTask * _Nullable task, NSError *error){
                                        if(errorCallback){
                                            errorCallback(@(error.code),error.localizedDescription,error);
                                        }
                                    }];
}

RCT_EXPORT_METHOD(httpPut:(NSString *)url
                  parameters:(id)parameters
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    
    __block RCTPromiseResolveBlock successCallback;
    if(resolve){
        successCallback = [resolve copy];
    }
    
    __block RCTPromiseRejectBlock errorCallback;
    if(reject){
        errorCallback = [reject copy];
    }
    
    [[NetworkingManager shareInstance] PUT:(NSString *)url
                                parameters:parameters
                                   success:^(NSURLSessionDataTask *task, id _Nullable responseObject){
                                       if(successCallback){
                                           successCallback(responseObject);
                                       }
                                   }
                                   failure:^(NSURLSessionDataTask * _Nullable task, NSError *error){
                                       if(errorCallback){
                                           errorCallback(@(error.code),error.localizedDescription,error);
                                       }
                                   }];
}

RCT_EXPORT_METHOD(httpPatch:(NSString *)url
                  parameters:(id)parameters
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    
    __block RCTPromiseResolveBlock successCallback;
    if(resolve){
        successCallback = [resolve copy];
    }
    
    __block RCTPromiseRejectBlock errorCallback;
    if(reject){
        errorCallback = [reject copy];
    }
    
    [[NetworkingManager shareInstance] PATCH:(NSString *)url
                                  parameters:parameters
                                     success:^(NSURLSessionDataTask *task, id _Nullable responseObject){
                                         if(successCallback){
                                             successCallback(responseObject);
                                         }
                                     }
                                     failure:^(NSURLSessionDataTask * _Nullable task, NSError *error){
                                         if(errorCallback){
                                             errorCallback(@(error.code),error.localizedDescription,error);
                                         }
                                     }];
}


RCT_EXPORT_METHOD(httpDelete:(NSString *)url
                  parameters:(id)parameters
                  resolver:(RCTPromiseResolveBlock)resolve
                  rejecter:(RCTPromiseRejectBlock)reject)
{
    
    __block RCTPromiseResolveBlock successCallback;
    if(resolve){
        successCallback = [resolve copy];
    }
    
    __block RCTPromiseRejectBlock errorCallback;
    if(reject){
        errorCallback = [reject copy];
    }
    
    [[NetworkingManager shareInstance] DELETE:(NSString *)url
                                   parameters:parameters
                                      success:^(NSURLSessionDataTask *task, id _Nullable responseObject){
                                          if(successCallback){
                                              successCallback(responseObject);
                                          }
                                      }
                                      failure:^(NSURLSessionDataTask * _Nullable task, NSError *error){
                                          if(errorCallback){
                                              errorCallback(@(error.code),error.localizedDescription,error);
                                          }
                                      }];
}

RCT_EXPORT_METHOD(putFile:(FileUploadInfo *)paramInfo resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
    __block RCTPromiseResolveBlock  successBlock = nil;
    if(resolve){
        successBlock =[resolve copy];
    }
    __block RCTPromiseRejectBlock  errorBlock = nil;
    if(reject){
        errorBlock=  [reject copy];
    }
    
    __weak typeof(self) weakSelf = self;
    if(paramInfo.fileType ==0){
        __block FileUploadInfo *uploadInfo = paramInfo;
        [weakSelf imageToUploadData:paramInfo.filePath size:paramInfo.size scale:paramInfo.scale withCompleted: ^(NSData *upData){
            __strong typeof (weakSelf)strongSelf = weakSelf;
            NSDictionary *parametersData =nil;
            /*
             NSDictionary *parametersData = @{
             @"objectKey": paramInfo.objectKey,
             @"storageType":[NSNumber numberWithInteger:paramInfo.storageType],
             @"FileType":[NSNumber numberWithInteger:paramInfo.fileType]
             };
             
             //http://10.2.33.36/
             [[NetworkingManager shareInstance] uploadFile:@"FileUploadService/api/upload"
             data:upData
             success:^(NSURLSessionDataTask *task, id _Nullable responseObject){
             if(successBlock){
             successBlock(responseObject);
             }
             }
             failure:^(NSURLSessionDataTask * _Nullable task, NSError *error){
             if(errorBlock){
             errorBlock(@(error.code),error.localizedDescription,error);
             }
             }];
             */
        }];
    }
}

RCT_EXPORT_METHOD(uploadFiles:(YuanXinUploadFiles *)paramInfo resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
    __block RCTPromiseResolveBlock  successBlock = nil;
    if(resolve){
        successBlock =[resolve copy];
    }
    __block RCTPromiseRejectBlock  errorBlock = nil;
    if(reject){
        errorBlock=  [reject copy];
    }
    
    __weak typeof(self) weakSelf = self;
    __block YuanXinUploadFiles *blockUpFiles = paramInfo;
    
    __block NSData *formBoundaryData = [[NSString stringWithFormat:@"--%@\r\n", blockUpFiles.resourceID] dataUsingEncoding:NSUTF8StringEncoding];
    __block  NSMutableData* reqBody = [NSMutableData data];
    
    // add fields
    [reqBody appendData:formBoundaryData];
    [reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"bucketName"] dataUsingEncoding:NSUTF8StringEncoding]];
    [reqBody appendData:[paramInfo.bucketName dataUsingEncoding:NSUTF8StringEncoding]];
    [reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    
    [reqBody appendData:formBoundaryData];
    [reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"storageType"] dataUsingEncoding:NSUTF8StringEncoding]];
    [reqBody appendData:[[[NSNumber numberWithInteger:paramInfo.storageType] stringValue] dataUsingEncoding:NSUTF8StringEncoding]];
    [reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    
    [reqBody appendData:formBoundaryData];
    [reqBody appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", @"resourceID"] dataUsingEncoding:NSUTF8StringEncoding]];
    [reqBody appendData:[paramInfo.resourceID dataUsingEncoding:NSUTF8StringEncoding]];
    [reqBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
    
    [[UploadFileLoader shareInstance] loaderFiles:blockUpFiles.files withCompleted:^(NSArray *results){
        
        __strong typeof(weakSelf) strongSelf = weakSelf;
        NSUInteger finishedCount = results.count;
        NSMutableString *errInfo = [NSMutableString stringWithString:@"文件不存在，请检查参数是否正确：文件 "];
        for (NSUInteger i=0; i<results.count; i++) {
            NSDictionary *item = results[i];
            if(![[item objectForKey:@"isError"] boolValue]){
                NSString *fileName = [NSString stringWithFormat:@"%@.%@",[item objectForKey:@"objectKey"],[item objectForKey:@"pathExtension"]];
                NSString *contentType = [[UploadFileLoader shareInstance] mimeTypeForFilePath:fileName];
                NSData *fileData = [item objectForKey:@"fileData"];
                NSData *data = [[UploadFileLoader shareInstance] appendFileFormData:fileData fileName:fileName contentType:contentType formBoundary:formBoundaryData];
                [reqBody appendData:data];
            } else {
                finishedCount--;
                [errInfo appendString:[NSString stringWithFormat:@"%lu ", (unsigned long)i]];
            }
        }
        
        if (finishedCount != results.count) {
            [errInfo appendString:@"参数有问题"];
            if(errorBlock){
                NSError *error = [NSError errorWithDomain:NSStringFromClass([self class]) code:-1 userInfo:@{NSLocalizedDescriptionKey: errInfo}];
                errorBlock(@(error.code).stringValue,error.localizedDescription,error);
            }
            return;
        }
        
        NSData* end = [[NSString stringWithFormat:@"--%@--\r\n", blockUpFiles.resourceID] dataUsingEncoding:NSUTF8StringEncoding];
        [reqBody appendData:end];
        
        NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", blockUpFiles.resourceID];
        NSDictionary *headrs = @{@"Content-Type":contentType,
                                 @"Accept":@"application/json"};
        
        
        [[NetworkingManager shareInstance] uploadFile:blockUpFiles.urlString
                                                 data:reqBody
                                              headers:headrs
                                              success:^(NSURLSessionDataTask *task, id _Nullable responseObject){
                                                  if(successBlock){
                                                      if(responseObject){
                                                          successBlock(responseObject);
                                                      }
                                                  }
                                              }
                                              failure:^(NSURLSessionDataTask * _Nullable task, NSError *error){
                                                  if(errorBlock){
                                                      errorBlock(@(error.code).stringValue,error.localizedDescription,error);
                                                  }
                                              }];
        
        
    }];
}

- (void)imageToUploadData:(NSString *)filePath size:(CGSize)size scale:(CGFloat)scale withCompleted:(void (^)(NSData *))completedBlock{
    
    void (^loadImageBlock)(NSError *error, UIImage *image) = ^(NSError *error, UIImage *image) {
        NSData *uploadData = nil;
        if (!error) {
            uploadData = UIImageJPEGRepresentation(image, scale);
        }else {
            RCTLogError(error);
        }
        if(completedBlock){
            completedBlock(uploadData);
        }
    };
    

    [_bridge.imageLoader loadImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:filePath]]
                                            size:size
                                           scale:scale
                                         clipped:YES
                                      resizeMode:RCTResizeModeStretch
                                   progressBlock:nil
#if __has_include(<React/RCTImageLoader.h>)
                                partialLoadBlock:nil
#endif
                                 completionBlock:loadImageBlock];
}

RCT_EXPORT_METHOD(removeImageCacheForUrl:(id)urlStr withSize:(CGSize)size) {
    // 导出给js的方法被循环调用时，rn 会释放掉之前的参数？？！！有很大风险造成野指针过度释放而崩溃
    /*
     index = 2;
     for (NSUInteger length = _invocation.methodSignature.numberOfArguments; index < length; index++) {
     if ([_invocation.methodSignature getArgumentTypeAtIndex:index][0] == _C_ID) {
             __unsafe_unretained id value;
             [_invocation getArgument:&value atIndex:index];
     
             if (value) {
                CFRelease((__bridge CFTypeRef)value);
             }
         }
     }
     */
    
    /*
     经测试不会崩溃的：
     1、NetworkingManager.removeImageCacheForUrl(userPhoto);
     
     2、NetworkingManager.removeImageCacheForUrl(userPhotoArray);
     
     3、NetworkingManager.removeImageCacheForUrl(userPhoto);
     NetworkingManager.removeImageCacheForUrl(userPhotoArray);
     
     会崩溃的：
     for (let item of result.members) {
        NetworkingManager.removeImageCacheForUrl(item.userPhoto);
     }
     */
    NSArray *urlStrArray = nil;
    if(urlStr) {
        if ([urlStr isKindOfClass:[NSString class]]) {
            urlStrArray = [NSArray arrayWithObject:urlStr];
        } else if ([urlStr isKindOfClass:[NSArray class]]) {
            urlStrArray = urlStr;
        }
    }
    for (NSString *_urlStr in urlStrArray) {
        [YuanXinImageCache removeSDImageCacheForUrl:_urlStr alsoWithSize:size];
    }
}

RCT_EXPORT_METHOD(clearAllImageCache){
    [YuanXinImageCache clearSDImageCacheDisk];
}

@end
